/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.connection;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Element;

import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;

/**
 * A command to reconnect a connection to a different start point or end point.
 * The command can be undone or redone.
 * <p>
 * This command is designed to be used together with a GraphicalNodeEditPolicy.
 * To use this command propertly, following steps are necessary:
 * </p>
 * <ol>
 * <li>Create a subclass of GraphicalNodeEditPolicy.</li>
 * <li>Override the <tt>getReconnectSourceCommand(...)</tt> method.
 * Here you need to obtain the Connection model element from the ReconnectRequest,
 * create a new ConnectionReconnectCommand, set the new connection <i>source</i> by calling
 * the <tt>setNewSource(Shape)</tt> method and return the command instance.
 * <li>Override the <tt>getReconnectTargetCommand(...)</tt> method.</li>
 * Here again you need to obtain the Connection model element from the ReconnectRequest,
 * create a new ConnectionReconnectCommand, set the new connection <i>target</i> by calling
 * the <tt>setNewTarget(Shape)</tt> method and return the command instance.</li>
 * </ol>
 * 
 * @see org.eclipse.gef.examples.shapes.parts.ShapeEditPart#createEditPolicies() for an
 * example of the above procedure.
 * @see org.eclipse.gef.editpolicies.GraphicalNodeEditPolicy
 * @see #setNewSource(Shape)
 * @see #setNewTarget(Shape)
 * @author Elias Volanakis
 */
public abstract class ConnectionReconnectCommandBase extends ConnectionCommandBase {

/**
 * The connection instance to reconnect.
 */
private GraphEdge edge;

/**
 * 
 */
private GraphConnector anchor;

/**
 * The old  endpoint.
 */
private GraphElement oldEndpoint;

/**
 * The new  endpoint.
 */
private GraphElement newEndpoint;

/**
 * anchor old location.
 */ 
private Point oldLocation;

/**
 * anchor new location.
 */ 
private Point newLocation;

/**
 * move edge command.
 */
private MoveAnchorPositionAndAttachedEdgesCommand moveAnchorCommand;

/**
 * 
 */
private int end;

/**
 * Instantiate a command that can reconnect a Connection instance to a different source
 * or target endpoint.
 * 
 * @param newEndpoint 
 * @param edge the connection instance to reconnect (non-null)
 * @param end 
 * @param newLocation 
 * 
 * @throws IllegalArgumentException if conn is null
 */
public ConnectionReconnectCommandBase(GraphEdge edge, GraphElement newEndpoint, Point newLocation, int end) {
	if (edge == null) {
		throw new IllegalArgumentException();
	}
	this.edge = edge;
	this.anchor = getEdgeAnchor(edge, end);
	this.oldEndpoint = anchor.getGraphElement();
	this.newEndpoint = newEndpoint;
	this.oldLocation = anchor.getPosition();
	this.newLocation = newLocation;
	this.end = end;
	
	if(end == SOURCE)
		setLabel("move connection startpoint");
	else
		setLabel("move connection endpoint");

}

/**
 * Reconnect the connection to newSource (if setNewSource(...) was invoked before)
 * or newTarget (if setNewTarget(...) was invoked before).
 */
public void execute() {
	// disconnect
	disconnectAnchor(anchor);

	// reconnect bussiness model if needed
	if( newEndpoint != oldEndpoint )
		{
		executeOnBussiness();
		}
	
	// move the anchor and all edges attached to the edge.
	// Take care to save old positions for undo
	// execute is called first time and after redo, so we test if the command already exist.
	if(moveAnchorCommand==null)
	{
	  moveAnchorCommand = new MoveAnchorPositionAndAttachedEdgesCommand(anchor);
	  moveAnchorCommand.savePositions();
	}
	moveAnchorCommand.moveAnchor(newLocation);
	// Reconnect anchor to its new parent
	reconnectAnchor(anchor, newEndpoint);
}

/**
 * Execute the command on the bussiness model.
 * This method is called only if the connected point move to a different node.
 * The default implementation ask for
 * disconnectOldBussinessModel()
 * reconnectNewBussinessModel()
 */
protected void executeOnBussiness() {
//	disconnectOldBussinessModel();
//	reconnectNewBussinessModel();
}

/**
 * Reconnect the connection to its original source and target endpoints.
 */
public void undo() {
	// disconnect
	disconnectAnchor(anchor);
	// reconnect bussiness model to old point
	// reconnect bussiness model to old point if needed
	if( newEndpoint != oldEndpoint )
		{
		undoOnBussiness();
		}
	  
	// restore anchor and attached edges old positions
	moveAnchorCommand.restorePositions();
	// Reconnect the anchor to its old parent.
	reconnectAnchor(anchor, oldEndpoint);
}

/**
 * Undo the command on the bussiness model.
 * This method is called only if the connected point have move to a different node.
 * The default implementation ask for
 * disconnectNewBussinessModel()
 * reconnectOldBussinessModel()
 */
protected void undoOnBussiness() {
//	disconnectNewBussinessModel();
//	reconnectOldBussinessModel();
}

///**
// * Disconnect the old bussiness model. The anchors are already disconnected.
// * Called when the command is executed/redo
// *
// */
//abstract protected  void disconnectOldBussinessModel();
//
///**
// * Connect the new bussiness model. The anchors will be reconnected after.
// * Called when the command is executed/redo
// *
// */
//abstract protected void reconnectNewBussinessModel();
//
///**
// * Disconnect the new bussiness model. The anchors are already disconnected.
// * Called when the command is undo
// *
// */
//abstract protected  void disconnectNewBussinessModel();
//
///**
// * Reconnect the old bussiness model. The anchors will be reconnected after.
// * Called when the command is undo
// *
// */
//abstract protected  void reconnectOldBussinessModel();



/**
 * Get the associated model element from the edge.
 * 
 * @param element  * @param value 
 */
protected void setEdgeElement(Element value)
{
	setBussinessElementFromGraphElement(edge, value);
}

/**
 * Get the associated model element from the source.
 * 
 * @param element  * 
 * @return 
 */
protected Element getEdgeElement()
{
	return getBussinessElementFromGraphElement(edge);
}

/**
 * Get the associated model element from the source.
 * 
 * @param element  * 
 * @return 
 */
protected Element getNewSourceElement()
{
	return getBussinessElementFromGraphElement(getNewSource());
}

/**
 * Get the associated model element from the target.
 * 
 * @param element  * @param value 
 */
protected void setNewSourceElement(Element value)
{
	setBussinessElementFromGraphElement(getNewSource(), value);
}


/**
 * Get the associated model element from the source.
 * 
 * @param element  * 
 * @return 
 */
protected Element getOldSourceElement()
{
	return getBussinessElementFromGraphElement(getOldSource());
}

/**
 * Get the associated model element from the target.
 * 
 * @param element  * @param value 
 */
protected void setOldSourceElement(Element value)
{
	setBussinessElementFromGraphElement(getOldSource(), value);
}


/**
 * Get the associated model element from the target.
 * 
 * @param element  * 
 * @return 
 */
protected Element getNewTargetElement()
{
	return getBussinessElementFromGraphElement(getNewTarget());
}

/**
 * Get the associated model element from the target.
 * 
 * @param element  * @param value 
 */
protected void setNewTargetElement(Element value)
{
	setBussinessElementFromGraphElement(getNewTarget(), value);
}

/**
 * Get the associated model element from the target.
 * 
 * @param element  * 
 * @return 
 */
protected Element getOldTargetElement()
{
	return getBussinessElementFromGraphElement(getOldTarget());
}

/**
 * Get the associated model element from the target.
 * 
 * @param element  * @param value 
 */
protected void setOldTargetElement(Element value)
{
	setBussinessElementFromGraphElement(getOldTarget(), value);
}

/**
 * 
 * 
 * @return 
 */
public GraphEdge getEdge() {
	return edge;
}

/**
 * 
 * 
 * @return 
 */
public GraphElement getOwner() {
	return getEdge().getContainer();
}

/**
 * 
 * 
 * @return 
 */
public GraphElement getNewSource() {
	
	if(isSourceReconnect())
		return newEndpoint;
	else
	   return getSource(edge);
}

/**
 * 
 * 
 * @return 
 */
public GraphElement getNewTarget() {
	if(isSourceReconnect())
		return getTarget(edge);
	else
	   return newEndpoint;
}

/**
 * 
 * 
 * @return 
 */
public GraphElement getOldSource() {
	if(isSourceReconnect())
		return oldEndpoint;
	else
	   return getSource(edge);
}

/**
 * 
 * 
 * @return 
 */
public GraphElement getOldTarget() {
	if(isSourceReconnect())
		return getTarget(edge);
	else
	   return oldEndpoint;
}

/**
 * 
 * 
 * @return 
 */
public int getEnd() {
	return end;
}

/**
 * 
 * 
 * @return 
 */
public boolean isSourceReconnect()
{
	return end==SOURCE;
}


}
